#include <stdio.h>
#include "nvs_flash.h"
#include "esp_event.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include <string.h>

// 程序功能：2023.4.2——ESP-IDF Event_loop处理方法学习
//-版本1：创建默认的event_loop队列
//      1.先创建一个默认event_loop队列
//      2.然后创建两个任务，一个任务对event_loop进行注册，另外一个任务用于发送post触发event_loop判断
//      3.注册的回调函数其实就是event_loop，系统分配给event_loop的堆栈很小，所以不能在回调函数中有大动作
//          只能在回调函数中创建另外的任务，用于进行接下来的操作
//      4.判断完id后，创建对应的任务进行接下来的操作，后删除自身，这部分代码没有体现
//-版本0：创建自己的event_loop队列
//      1.先创建一个event_loop队列
//      2.然后对队列所需的事件和id进行注册：event_loop_handler，事件类型，事件ID，触发进行的函数，触发是事件类型时传输的数据
//      3.在Task_post进行对注册的任务post来触发事件：loop句柄，事件类型，事件类型id，触发id发送的数据，数据长度，等待时间
//      4.可以在触发事件后回调函数中进行id的判断

// 对比下来自己创建的：1.多了对event_loop的设置、注册时也多了event_loop_handle
//                   2.在进行注册时推荐id号为ESP_EVENT_ANY_ID，在回调函数中进行switch的判断
//                   3.post时也多了event_loop_handle

#define Version_test 0
void Task_event_register(void *xtask);
void Task_post(void *xtask);
void event_handle_function(void *handler_arg, esp_event_base_t base, int32_t id, void *event_data);
esp_event_loop_handle_t event_loop_handler; // event_loop句柄
esp_event_base_t task_base = "Event_loop类型";

typedef enum
{
    event1 = 0,
    event2,
    event3,
} event_t;

/*在event_loop中注册两个事件*/
void Task_event_register(void *xtask)
{
#if (Version_test == 0)
    ESP_LOGE("注册任务", "Task_post");
    //-在event_loop中注册了两个event_base，传进两个需要的id号，现已优化为1个
    esp_event_handler_register_with(event_loop_handler,    // event_loop句柄
                                    task_base,             // event_loop事件类型
                                    ESP_EVENT_ANY_ID,      // 事件ID号
                                    event_handle_function, // 触发event_loop事件类型中的event1后执行的操作函数
                                    "event_handler_arg");
    // esp_event_handler_register_with(event_loop_handler,    // event_loop句柄
    //                                 task_base,             // event_loop事件类型
    //                                 event2,                // 事件ID号
    //                                 event_handle_function, // 触发event_loop事件类型中的event1后执行的操作函数
    //                                 "event_handler_arg");
#elif (Version_test == 1)
    ESP_LOGI("register_TASK", "Task_event_register 创建完成");
    char *event_type_data = "event_handler_arg";
    esp_event_handler_register(task_base, ESP_EVENT_ANY_ID, event_handle_function, event_type_data);

#endif
    static char InfoBuffer[512] = {0};
    while (1)
    {
        // vTaskGetRunTimeStats((char *)&InfoBuffer);
        // printf("\r\n任务名       运行计数         使用率\r\n");
        // printf("\r\n%s\r\n", InfoBuffer);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

/*发送注册事件的post*/
void Task_post(void *xtask)
{
    ESP_LOGE("发送任务", "准备发送post了");

#if (Version_test == 0)
    vTaskDelay(5000 / portTICK_PERIOD_MS);
    ESP_LOGE("Task_post", "发送event1的post了");
    char *event_id_date = "任务1";
    esp_event_post_to(event_loop_handler,        // 发送到哪个event_loop_handler中
                      task_base,                 // 发送的事件类型
                      event1,                    // 发送的事件类型号
                      event_id_date,             // 发送的event_data数据
                      strlen(event_id_date) + 1, // 发送的event_data数据的长度
                      portMAX_DELAY);            // 无限期等待
    vTaskDelay(5000 / portTICK_PERIOD_MS);

    ESP_LOGE("Task_post", "发送event2的post了");
    event_id_date = "任务2";
    esp_event_post_to(event_loop_handler,        // 发送到哪个event_loop_handler中
                      task_base,                 // 发送的事件类型
                      event2,                    // 发送的事件类型号
                      event_id_date,             // 发送的event_data数据
                      strlen(event_id_date) + 1, // 发送的event_data数据的长度
                      portMAX_DELAY);            // 无限期等待
    vTaskDelay(5000 / portTICK_PERIOD_MS);
#elif (Version_test == 1)
    vTaskDelay(5000 / portTICK_PERIOD_MS);
    ESP_LOGE("Task_post", "发送event1的post了");
    char *event_id_date = "任务1";
    //-post发送到默认创建的event_loop中
    esp_event_post(task_base,                 // 发送的事件类型
                   event1,                    // 发送的事件类型号
                   event_id_date,             // 发送的event_data数据
                   strlen(event_id_date) + 1, // 发送的event_data数据的长度
                   portMAX_DELAY);            // 无限期等待
    vTaskDelay(5000 / portTICK_PERIOD_MS);

    ESP_LOGE("Task_post", "发送event2的post了");
    event_id_date = "任务2";
    esp_event_post(task_base,                 // 发送的事件类型
                   event2,                    // 发送的事件类型号
                   event_id_date,             // 发送的event_data数据
                   strlen(event_id_date) + 1, // 发送的event_data数据的长度
                   portMAX_DELAY);            // 无限期等待
    vTaskDelay(5000 / portTICK_PERIOD_MS);

#endif

    vTaskDelete(NULL);
}

/*触发事件后回调函数*/
void event_handle_function(void *handler_arg, esp_event_base_t base, int32_t id, void *event_data)
{
#if (Version_test == 0)
    char *msg_from_event_base = (char *)handler_arg;
    char *msg_from_event_id = (char *)event_data;

    switch (id)
    {
    case event1:
        ESP_LOGE("EVENT_HANDLE", "触发事件类型要发送的数据：%s！触发事件ID发送的数据：%s！事件类型：%s！事件ID：%d！", msg_from_event_base, msg_from_event_id, base, id);
        ESP_LOGE("EVENT_HANDLE", "任务event1创建完成，其余工作由event1_task完成\n");

        break;
    case event2:
        ESP_LOGE("EVENT_HANDLE", "触发事件类型要发送的数据：%s！触发事件ID发送的数据：%s！事件类型：%s！事件ID：%d！", msg_from_event_base, msg_from_event_id, base, id);
        ESP_LOGE("EVENT_HANDLE", "任务event2创建完成，其余工作由event2_task完成\n");
        break;
    default:
        break;
    }

#elif (Version_test == 1)
    ESP_LOGI("EVENT_HANDLE", "BASE:%s, ID:%d", base, id);
    ESP_LOGI("EVENT_HANDLE", "此时函数是event_loop中，所以不能有大动作操作的");

    switch (id)
    {
    case event1:
        ESP_LOGE("EVENT_HANDLE", "任务event1创建完成，其余工作由event1_task完成\n");
        break;
    case event2:
        ESP_LOGE("EVENT_HANDLE", "任务event2创建完成，其余工作由event2_task完成\n");
        break;
    default:
        break;
    }

#endif
}

void app_main(void)
{
#if (Version_test == 0)
    /*
    课程开始前，我们先回顾一下
    char_pointer - 指针指向的地址
    %char_pointer - 指针自己的地址
    sizeof(char_pointer) - 指针本身的大小
    strlen(char_pointer) - 指针指向的char数组的大小,不包括最后的NULL
    strlen(char_pointer)+1 - 指针指向的char数组的大小,包括最后的NULL
    */
    char *char_pointer = "1234567890";
    ESP_LOGI("main", "char_pointer 内容: %s", char_pointer);
    ESP_LOGI("main", "char_pointer %p", char_pointer); //%p输出指针地址
    ESP_LOGI("main", "&char_pointer %p", &char_pointer);
    ESP_LOGI("main", "sizeof(char_pointer) %d", sizeof(char_pointer));
    ESP_LOGI("main", "strlen(char_pointer) %d", strlen(char_pointer));

    //-创建event_loop队列
    ESP_LOGE("MAIN", "创建 Event Loop");
    esp_event_loop_args_t self_event_loop_args = {
        .queue_size = 10,                         // loop长度是5
        .task_name = "night_market_task",         // 任务的名字
        .task_priority = uxTaskPriorityGet(NULL), // event_loop的优先级
        .task_stack_size = 1024 * 15,             // event_loop的堆栈大小
        .task_core_id = tskNO_AFFINITY,           // event_loop任务创建在哪个核心上
    };
    //=event_loop创建函数，填入loop和对应的句柄
    ESP_ERROR_CHECK(esp_event_loop_create(&self_event_loop_args, &event_loop_handler));

    // 创建两个任务
    xTaskCreate(Task_post, NULL, 1024 * 12, NULL, 1, NULL);
    xTaskCreate(Task_event_register, NULL, 1024 * 12, NULL, 1, NULL);

#elif (Version_test == 1)
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_ERROR_CHECK(esp_event_loop_create_default());

    xTaskCreate(Task_event_register, NULL, 1024 * 12, NULL, 1, NULL);
    xTaskCreate(Task_post, NULL, 1024 * 12, NULL, 1, NULL);
#endif
    vTaskDelete(NULL);
}
